"""
AIQuant Web缓存管理器
提供统一的缓存机制，支持TTL过期、装饰器、Session state集成等功能
"""

import hashlib
import json
import logging
import time
from functools import wraps
from typing import Any, Callable, Dict, Optional, Union

import streamlit as st

logger = logging.getLogger(__name__)


class CacheManager:
    """
    缓存管理器
    
    提供以下功能：
    - 基于Session state的缓存存储
    - TTL过期机制
    - 缓存键自动生成
    - 缓存统计和监控
    - 缓存清理机制
    """
    
    def __init__(self, default_ttl: int = 300):
        """
        初始化缓存管理器
        
        Args:
            default_ttl: 默认TTL时间（秒）
        """
        self.default_ttl = default_ttl
        self.cache_prefix = "aiquant_cache"
        
        # 初始化Session state中的缓存存储
        if f"{self.cache_prefix}_data" not in st.session_state:
            st.session_state[f"{self.cache_prefix}_data"] = {}
        
        if f"{self.cache_prefix}_stats" not in st.session_state:
            st.session_state[f"{self.cache_prefix}_stats"] = {
                'hits': 0,
                'misses': 0,
                'sets': 0,
                'deletes': 0,
                'total_size': 0
            }
    
    def _get_cache_data(self) -> Dict[str, Any]:
        """获取缓存数据存储"""
        return st.session_state[f"{self.cache_prefix}_data"]
    
    def _get_cache_stats(self) -> Dict[str, Any]:
        """获取缓存统计"""
        return st.session_state[f"{self.cache_prefix}_stats"]
    
    def _generate_key(self, *args, **kwargs) -> str:
        """
        生成缓存键
        
        Args:
            *args: 位置参数
            **kwargs: 关键字参数
            
        Returns:
            MD5哈希键
        """
        # 创建包含所有参数的字符串
        key_data = {
            'args': args,
            'kwargs': sorted(kwargs.items()) if kwargs else {}
        }
        
        # 序列化为JSON字符串
        key_str = json.dumps(key_data, sort_keys=True, default=str)
        
        # 生成MD5哈希
        return hashlib.md5(key_str.encode()).hexdigest()
    
    def _is_expired(self, cache_entry: Dict[str, Any]) -> bool:
        """
        检查缓存项是否过期
        
        Args:
            cache_entry: 缓存项
            
        Returns:
            是否过期
        """
        if 'expires_at' not in cache_entry:
            return False
        
        return time.time() > cache_entry['expires_at']
    
    def get(self, key: str) -> Optional[Any]:
        """
        获取缓存值
        
        Args:
            key: 缓存键
            
        Returns:
            缓存值，如果不存在或过期则返回None
        """
        cache_data = self._get_cache_data()
        stats = self._get_cache_stats()
        
        if key not in cache_data:
            stats['misses'] += 1
            logger.debug(f"缓存未命中: {key}")
            return None
        
        cache_entry = cache_data[key]
        
        # 检查是否过期
        if self._is_expired(cache_entry):
            del cache_data[key]
            stats['misses'] += 1
            stats['deletes'] += 1
            logger.debug(f"缓存过期: {key}")
            return None
        
        stats['hits'] += 1
        logger.debug(f"缓存命中: {key}")
        return cache_entry['value']
    
    def set(self, key: str, value: Any, ttl: Optional[int] = None) -> None:
        """
        设置缓存值
        
        Args:
            key: 缓存键
            value: 缓存值
            ttl: TTL时间（秒），None表示使用默认TTL
        """
        cache_data = self._get_cache_data()
        stats = self._get_cache_stats()
        
        ttl = ttl if ttl is not None else self.default_ttl
        expires_at = time.time() + ttl if ttl > 0 else None
        
        cache_entry = {
            'value': value,
            'created_at': time.time(),
            'expires_at': expires_at,
            'ttl': ttl
        }
        
        cache_data[key] = cache_entry
        stats['sets'] += 1
        stats['total_size'] = len(cache_data)
        
        logger.debug(f"缓存设置: {key} (TTL: {ttl}s)")
    
    def delete(self, key: str) -> bool:
        """
        删除缓存项
        
        Args:
            key: 缓存键
            
        Returns:
            是否成功删除
        """
        cache_data = self._get_cache_data()
        stats = self._get_cache_stats()
        
        if key in cache_data:
            del cache_data[key]
            stats['deletes'] += 1
            stats['total_size'] = len(cache_data)
            logger.debug(f"缓存删除: {key}")
            return True
        
        return False
    
    def clear(self) -> None:
        """清空所有缓存"""
        cache_data = self._get_cache_data()
        stats = self._get_cache_stats()
        
        count = len(cache_data)
        cache_data.clear()
        stats['deletes'] += count
        stats['total_size'] = 0
        
        logger.info(f"清空缓存: {count}项")
    
    def cleanup_expired(self) -> int:
        """
        清理过期缓存项
        
        Returns:
            清理的项目数量
        """
        cache_data = self._get_cache_data()
        stats = self._get_cache_stats()
        
        expired_keys = []
        for key, cache_entry in cache_data.items():
            if self._is_expired(cache_entry):
                expired_keys.append(key)
        
        for key in expired_keys:
            del cache_data[key]
        
        count = len(expired_keys)
        stats['deletes'] += count
        stats['total_size'] = len(cache_data)
        
        if count > 0:
            logger.info(f"清理过期缓存: {count}项")
        
        return count
    
    def get_stats(self) -> Dict[str, Any]:
        """
        获取缓存统计信息
        
        Returns:
            统计信息字典
        """
        stats = self._get_cache_stats().copy()
        cache_data = self._get_cache_data()
        
        # 计算命中率
        total_requests = stats['hits'] + stats['misses']
        hit_rate = stats['hits'] / total_requests if total_requests > 0 else 0
        
        # 添加额外统计
        stats.update({
            'hit_rate': hit_rate,
            'total_requests': total_requests,
            'current_size': len(cache_data)
        })
        
        return stats
    
    def get_cache_info(self) -> Dict[str, Any]:
        """
        获取详细缓存信息
        
        Returns:
            缓存详细信息
        """
        cache_data = self._get_cache_data()
        current_time = time.time()
        
        cache_info = {
            'total_items': len(cache_data),
            'expired_items': 0,
            'items_by_ttl': {},
            'oldest_item': None,
            'newest_item': None
        }
        
        oldest_time = float('inf')
        newest_time = 0
        
        for key, cache_entry in cache_data.items():
            created_at = cache_entry.get('created_at', 0)
            ttl = cache_entry.get('ttl', 0)
            
            # 统计过期项
            if self._is_expired(cache_entry):
                cache_info['expired_items'] += 1
            
            # 按TTL分组
            ttl_key = f"{ttl}s" if ttl > 0 else "永久"
            cache_info['items_by_ttl'][ttl_key] = cache_info['items_by_ttl'].get(ttl_key, 0) + 1
            
            # 找到最老和最新的项
            if created_at < oldest_time:
                oldest_time = created_at
                cache_info['oldest_item'] = {
                    'key': key,
                    'age': current_time - created_at
                }
            
            if created_at > newest_time:
                newest_time = created_at
                cache_info['newest_item'] = {
                    'key': key,
                    'age': current_time - created_at
                }
        
        return cache_info


# 全局缓存管理器实例
_cache_manager: Optional[CacheManager] = None


def get_cache_manager() -> CacheManager:
    """
    获取全局缓存管理器实例
    
    Returns:
        缓存管理器实例
    """
    global _cache_manager
    
    if _cache_manager is None:
        _cache_manager = CacheManager()
    
    return _cache_manager


def cached_api_call(ttl: int = 300, key_prefix: str = "api"):
    """
    API调用缓存装饰器
    
    Args:
        ttl: 缓存TTL时间（秒）
        key_prefix: 缓存键前缀
        
    Returns:
        装饰器函数
    """
    def decorator(func: Callable) -> Callable:
        @wraps(func)
        def wrapper(*args, **kwargs):
            cache_manager = get_cache_manager()
            
            # 生成缓存键
            cache_key = f"{key_prefix}_{func.__name__}_{cache_manager._generate_key(*args, **kwargs)}"
            
            # 尝试从缓存获取
            cached_result = cache_manager.get(cache_key)
            if cached_result is not None:
                logger.debug(f"使用缓存结果: {func.__name__}")
                return cached_result
            
            # 执行函数并缓存结果
            try:
                result = func(*args, **kwargs)
                cache_manager.set(cache_key, result, ttl)
                logger.debug(f"缓存函数结果: {func.__name__} (TTL: {ttl}s)")
                return result
            except Exception as e:
                logger.error(f"函数执行失败: {func.__name__} - {str(e)}")
                raise
        
        return wrapper
    return decorator


def cache_key(*args, **kwargs) -> str:
    """
    生成缓存键的便捷函数
    
    Args:
        *args: 位置参数
        **kwargs: 关键字参数
        
    Returns:
        缓存键
    """
    cache_manager = get_cache_manager()
    return cache_manager._generate_key(*args, **kwargs)


def clear_cache(pattern: Optional[str] = None) -> int:
    """
    清理缓存
    
    Args:
        pattern: 键模式，None表示清理所有
        
    Returns:
        清理的项目数量
    """
    cache_manager = get_cache_manager()
    
    if pattern is None:
        cache_manager.clear()
        return cache_manager.get_stats()['total_size']
    else:
        # TODO: 实现模式匹配清理
        return cache_manager.cleanup_expired()


def get_cache_stats() -> Dict[str, Any]:
    """获取缓存统计信息"""
    cache_manager = get_cache_manager()
    return cache_manager.get_stats()


if __name__ == "__main__":
    # 测试代码
    def test_cache_manager():
        cache_manager = CacheManager(default_ttl=5)
        
        # 测试基本操作
        cache_manager.set("test_key", "test_value", 10)
        value = cache_manager.get("test_key")
        print(f"缓存值: {value}")
        
        # 测试装饰器
        @cached_api_call(ttl=5, key_prefix="test")
        def expensive_function(x, y):
            print(f"执行昂贵计算: {x} + {y}")
            return x + y
        
        # 第一次调用
        result1 = expensive_function(1, 2)
        print(f"结果1: {result1}")
        
        # 第二次调用（应该使用缓存）
        result2 = expensive_function(1, 2)
        print(f"结果2: {result2}")
        
        # 获取统计信息
        stats = cache_manager.get_stats()
        print(f"缓存统计: {stats}")
        
        # 获取详细信息
        info = cache_manager.get_cache_info()
        print(f"缓存信息: {info}")
    
    # 运行测试（需要在Streamlit环境中）
    # test_cache_manager()